gusucode.com > VC 点对点文件传输实例 > VC 点对点文件传输实例/code/www.NewXing.com/Server_FileTransfer/TCPServer_FT.cpp

    // TCPServer_FT.cpp: implementation of the CTCPServer_FT class.
//////////////////////////////////////////////////////////////////////
// Download by http://www.NewXing.com
#include "stdafx.h"
#include "TCPServer_FT.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction//类的实现
//////////////////////////////////////////////////////////////////////

CTCPServer_FT::CTCPServer_FT()
{

}

CTCPServer_FT::~CTCPServer_FT()
{

}

//重载构造函数,添加参数:指向窗口的指针
CTCPServer_FT::CTCPServer_FT(LPVOID ptr)
{
	m_iListenPort = 7000;  
	m_bEndListenThread = TRUE;//停止监听
	m_pWnd = (CWnd*)ptr;
}
//功能:设置监听端口
//参数:端口号
//返回:无
void CTCPServer_FT::SetListenPort(int iPort)
{
	m_iListenPort = iPort;
}

//功能:开始监听客户端文件请求
//参数:指向接受消息的窗口的指针
//返回:无
void CTCPServer_FT::ListenRequest()
{
	m_bEndListenThread = FALSE;//开始监听

	DWORD id;
	HANDLE h = CreateThread(NULL, 0, ThreadListen, this, 0, &id);//创建句柄
	//线程要执行的函数为ThreadListen,将this指针传递给线程,线程ID为id,创建成功,返回新线程的一个句柄
	CloseHandle(h); //关闭句柄,并不终止线程
}

//功能:停止监听
//参数:无
//返回:无
void CTCPServer_FT::StopListen()
{
	m_bEndListenThread = TRUE;
}

//功能:监听线程
//参数:LPVOID lpParam
//返回:无意义
DWORD WINAPI CTCPServer_FT::ThreadListen(LPVOID lpParam)
{
	CTCPServer_FT* pServer = (CTCPServer_FT*)lpParam;

	SOCKET sockListen = socket(AF_INET, SOCK_STREAM, 0);//创建socket
	//地址家族为internet,用于TCP和UDP,字节流,特定的协议

	SOCKADDR_IN sin; //转换地址
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = 0; //ip地址,以网路字节顺序。

	sin.sin_port = htons(pServer->m_iListenPort);
	//端口号,从主机字节顺序转化成网络字节顺序

	if(bind(sockListen, (SOCKADDR*)&sin, sizeof(sin)) == SOCKET_ERROR)
	{//若将socket与本地地址绑定失败
		closesocket(sockListen); //关闭socket
		pServer->m_pWnd->SendMessage(WM_BINDERROR);//显示错误
		return 1;
	}

	if(listen(sockListen, 5) == SOCKET_ERROR)
	{//若将socket置于监听失败
		closesocket(sockListen);
		pServer->m_pWnd->SendMessage(WM_LISTENERROR);
		return 1;
	}
	//开始监听
	pServer->m_pWnd->SendMessage(WM_STARTLISTEN);

	fd_set fdListen;//结构体,将socket放进一个集合
	timeval seltime;//结构体,指定时间值(秒+毫秒)
	seltime.tv_sec = 0; //秒
	seltime.tv_usec = 10000;//毫秒==10秒

	while(!pServer->m_bEndListenThread)
	{//服务器在监听状态
		FD_ZERO(&fdListen);//将集合清零
		FD_SET(sockListen, &fdListen);//将socket添加到set中

		if(select(0, &fdListen, NULL, NULL, &seltime) <= 0 || !FD_ISSET(sockListen, &fdListen) )
			continue;
		//如果在规定时间内,set中无socket可读出||socket不在set中,结束本次循环,继续下次循环

		int len = sizeof(sin);
		SOCKET sock = accept(sockListen, (SOCKADDR*)&sin, &len);
		//接受socket上的连接
		
		PARAMRECV* pParamRecv = new PARAMRECV;//结构体,接收线程参数
		pParamRecv->sock = sock;
		pParamRecv->ptr = pServer;

		DWORD id;
		HANDLE h = CreateThread(NULL, 0, ThreadRecv, pParamRecv, 0, &id);
		//创建ThreadRecv线程,将pParamRecv传递给线程
		CloseHandle(h);
	}
	closesocket(sockListen);
	return 0;
}


//功能:接收线程
//参数:LPVOID lpParam
//返回:无意义
DWORD WINAPI CTCPServer_FT::ThreadRecv(LPVOID lpParam)
{
	PARAMRECV* pParam = (PARAMRECV*)lpParam;//指向接收线程的指针
	CTCPServer_FT* pServer = (CTCPServer_FT*)pParam->ptr;//指向Server的指针
	
	fd_set fdRecv;//结构体,接收线程集合
	timeval seltime;//结构体,指定时间值==10s
	seltime.tv_sec = 0;
	seltime.tv_usec = 10000;
	
	while(1)
	{
		FD_ZERO(&fdRecv);//集合清零
		FD_SET(pParam->sock, &fdRecv);//将已连接的socket加入集合
		
		if(select(0, &fdRecv, NULL, NULL, &seltime) <= 0 || !FD_ISSET(pParam->sock, &fdRecv) )
			continue;
		//如果在指定时间内,结构体内无可读接收线程||sock不在结构体内
		
		MSGREQUEST msgRequest;//结构体,C--S通信缓冲区
		int iRecvCnt = recv(pParam->sock, (char*)&msgRequest, sizeof(msgRequest), 0);
		//成功,返回接收字节数;正常结束,返回0;否则<0
		if(iRecvCnt <= 0)//不成功
			break;// 结束
		
		//如果是请求文件列表
		if(msgRequest.iCommand == FILELIST)
		{
			pServer->m_pWnd->SendMessage(WM_SENDFILELIST, (WPARAM)&pParam->sock, 0);
			//发送指定信息到窗口
		}
		
		//如果是请求文件数据
		else if(msgRequest.iCommand == FILEDATA)
		{
			long lFileOffset = msgRequest.lFileOffset;//文件偏移

			CFile file;
			BOOL bResult = file.Open(msgRequest.sServerPath, CFile::modeRead|CFile::shareDenyNone, NULL);
			//打开指定路径的文件,只读||可共享,成功,返回非零;否则,0
			if(!bResult)
			{
				break;		//如果文件打开失败就终止线程
			}

			char sSendBuf[SENDSIZE];//发送缓冲区,16K			
			while(lFileOffset < msgRequest.lFileLength)
			{//当文件偏移<文件长度时
				int iSeek = file.Seek(lFileOffset, CFile::begin);//寻找新的文件偏移,距离开始处
				int iReadCnt = file.Read(sSendBuf, SENDSIZE);//读计数器,读到发送缓冲区,一次16K
				if(iReadCnt ==0)//读出字节数为0
					break;

				int iSendCnt = send(pParam->sock, sSendBuf, iReadCnt, 0);
				//将读出的数据发送至已连接的socket,成功返回发送的字节数	
				//如果发送失败就终止线程
				if(iSendCnt == -1)
					break;
				else
					lFileOffset += iSendCnt;//修改偏移量
			}//end of while(lFileOffset < lFileLength)
			
			file.Close();
		}//end of else if(msgRequest.iCommand == FILEDATA)

		break;
	}//end of while(1)	
	closesocket(pParam->sock);//关闭socket
	delete pParam;//删除接收线程指针
	return 0;
}